/*
* @(#)AbstractFigure.java 5.1
*
*/
package CH.ifa.draw.standard;
import CH.ifa.draw.util.*;
import CH.ifa.draw.framework.*;
import java.awt.*;
import java.util.*;
import java.io.*;
/**
* AbstractFigure provides default implementations for
* the Figure interface.
*
* <hr>
* <b>Design Patterns</b><P>
* <img src="images/red-ball-small.gif" width=6 height=6 alt=" o ">
* <b><a href=../pattlets/sld036.htm>Template Method</a></b><br>
* Template Methods implement default and invariant behavior for
* figure subclasses.
* <hr>
*
* @see Figure
* @see Handle
*/
public abstract class AbstractFigure implements Figure {
/**
* The listeners for a figure's changes.
* @see #invalidate
* @see #changed
* @see #willChange
*/
private transient FigureChangeListener fListener;
/*
* Serialization support.
*/
private static final long serialVersionUID = -10857585979273442L;
private int abstractFigureSerializedDataVersion = 1;
protected AbstractFigure() { }
/**
* Moves the figure by the given offset.
*/
public void moveBy(int dx, int dy) {
willChange();
basicMoveBy(dx, dy);
changed();
}
/**
* Moves the figure. This is the
* method that subclassers override. Clients usually
* call displayBox.
* @see moveBy
*/
protected abstract void basicMoveBy(int dx, int dy);
/**
* Changes the display box of a figure. Clients usually
* call this method. It changes the display box
* and announces the corresponding change.
* @param origin the new origin
* @param corner the new corner
* @see displayBox
*/
public void displayBox(Point origin, Point corner) {
willChange();
basicDisplayBox(origin, corner);
changed();
}
/**
* Sets the display box of a figure. This is the
* method that subclassers override. Clients usually
* call displayBox.
* @see displayBox
*/
public abstract void basicDisplayBox(Point origin, Point corner);
/**
* Gets the display box of a figure.
*/
public abstract Rectangle displayBox();
/**
* Returns the handles of a Figure that can be used
* to manipulate some of its attributes.
* @return a Vector of handles
* @see Handle
*/
public abstract Vector<Handle> handles();
/**
* Returns an Enumeration of the figures contained in this figure.
* @see CompositeFigure
*/
public FigureEnumeration figures() {
Vector figures = new Vector(1);
figures.addElement(this);
return new FigureEnumerator(figures);
}
/**
* Gets the size of the figure. A convenience method.
*/
public Dimension size() {
return new Dimension(displayBox().width, displayBox().height);
}
/**
* Checks if the figure is empty. The default implementation returns
* true if the width or height of its display box is < 3
* @see Figure#isEmpty
*/
public boolean isEmpty() {
return (size().width < 3) || (size().height < 3);
}
/**
* Returns the figure that contains the given point.
* In contrast to containsPoint it returns its
* innermost figure that contains the point.
*
* @see #containsPoint
*/
public Figure findFigureInside(int x, int y) {
if (containsPoint(x, y))
return this;
return null;
}
/**
* Checks if a point is inside the figure.
*/
public boolean containsPoint(int x, int y) {
return displayBox().contains(x, y);
}
/**
* Changes the display box of a figure. This is a
* convenience method. Implementors should only
* have to override basicDisplayBox
* @see displayBox
*/
public void displayBox(Rectangle r) {
displayBox(new Point(r.x, r.y), new Point(r.x+r.width, r.y+r.height));
}
/**
* Checks whether the given figure is contained in this figure.
*/
public boolean includes(Figure figure) {
return figure == this;
}
/**
* Decomposes a figure into its parts. It returns a Vector
* that contains itself.
* @return an Enumeration for a Vector with itself as the
* only element.
*/
public FigureEnumeration decompose() {
Vector figures = new Vector(1);
figures.addElement(this);
return new FigureEnumerator(figures);
}
/**
* Sets the Figure's container and registers the container
* as a figure change listener. A figure's container can be
* any kind of FigureChangeListener. A figure is not restricted
* to have a single container.
*/
public void addToContainer(FigureChangeListener c) {
addFigureChangeListener(c);
invalidate();
}
/**
* Removes a figure from the given container and unregisters
* it as a change listener.
*/
public void removeFromContainer(FigureChangeListener c) {
invalidate();
removeFigureChangeListener(c);
changed();
}
/**
* Adds a listener for this figure.
*/
public void addFigureChangeListener(FigureChangeListener l) {
fListener = FigureChangeEventMulticaster.add(fListener, l);
}
/**
* Removes a listener for this figure.
*/
public void removeFigureChangeListener(FigureChangeListener l) {
fListener = FigureChangeEventMulticaster.remove(fListener, l);
}
/**
* Gets the figure's listners.
*/
public FigureChangeListener listener() {
return fListener;
}
/**
* A figure is released from the drawing. You never call this
* method directly. Release notifies its listeners.
* @see Figure#release
*/
public void release() {
if (fListener != null)
fListener.figureRemoved(new FigureChangeEvent(this));
}
/**
* Invalidates the figure. This method informs the listeners
* that the figure's current display box is invalid and should be
* refreshed.
*/
public void invalidate() {
if (fListener != null) {
Rectangle r = displayBox();
r.grow(Handle.HANDLESIZE, Handle.HANDLESIZE);
fListener.figureInvalidated(new FigureChangeEvent(this, r));
}
}
/**
* Informes that a figure is about to change something that
* affects the contents of its display box.
*
* @see Figure#willChange
*/
public void willChange() {
invalidate();
}
/**
* Informs that a figure changed the area of its display box.
*
* @see FigureChangeEvent
* @see Figure#changed
*/
public void changed() {
invalidate();
if (fListener != null)
fListener.figureChanged(new FigureChangeEvent(this));
}
/**
* Gets the center of a figure. A convenice
* method that is rarely overridden.
*/
public Point center() {
return Geom.center(displayBox());
}
/**
* Checks if this figure can be connected. By default
* AbstractFigures can be connected.
*/
public boolean canConnect() {
return true;
}
/**
* Returns the connection inset. The connection inset
* defines the area where the display box of a
* figure can't be connected. By default the entire
* display box can be connected.
*
*/
public Insets connectionInsets() {
return new Insets(0, 0, 0, 0);
}
/**
* Returns the Figures connector for the specified location.
* By default a ChopBoxConnector is returned.
* @see ChopBoxConnector
*/
public Connector connectorAt(int x, int y) {
return new ChopBoxConnector(this);
}
/**
* Sets whether the connectors should be visible.
* By default they are not visible and
*/
public void connectorVisibility(boolean isVisible) {
}
/**
* Returns the locator used to located connected text.
*/
public Locator connectedTextLocator(Figure text) {
return RelativeLocator.center();
}
/**
* Returns the named attribute or null if a
* a figure doesn't have an attribute.
* By default
* figures don't have any attributes getAttribute
* returns null.
*/
public Object getAttribute(String name) {
return null;
}
/**
* Sets the named attribute to the new value. By default
* figures don't have any attributes and the request is ignored.
*/
public void setAttribute(String name, Object value) {
}
/**
* Clones a figure. Creates a clone by using the storable
* mechanism to flatten the Figure to stream followed by
* resurrecting it from the same stream.
*
* @see Figure#clone
*/
public Object clone() {
Object clone = null;
ByteArrayOutputStream output = new ByteArrayOutputStream(200);
try {
ObjectOutput writer = new ObjectOutputStream(output);
writer.writeObject(this);
writer.close();
} catch (IOException e) {
System.out.println("Class not found: " + e);
}
InputStream input = new ByteArrayInputStream(output.toByteArray());
try {
ObjectInput reader = new ObjectInputStream(input);
clone = (Object) reader.readObject();
} catch (IOException e) {
System.out.println(e.toString());
}
catch (ClassNotFoundException e) {
System.out.println("Class not found: " + e);
}
return clone;
}
/**
* Stores the Figure to a StorableOutput.
*/
public void write(StorableOutput dw) {
}
/**
* Reads the Figure from a StorableInput.
*/
public void read(StorableInput dr) throws IOException {
}
}